package model

import (
	"gopkg.in/mgo.v2/bson"
	"log"
	"src/config"
	newtime "src/util/time"
	"sync"
)

type ForumPost struct {
	ID     bson.ObjectId `bson:"_id,omitempty" json:"id,omitempty"`
	Status int           `bson:"status" json:"status"`

	Type     int   `bson:"type" json:"type"`
	TypeTags []int `bson:"type_tags" json:"type_tags"`

	Tags           []int    `bson:"tags" json:"tags"`
	UserID         string   `bson:"user_id" json:"userid"`
	Text           string   `bson:"text" json:"text"`
	Anonymity      bool     `bson:"anonymity" json:"anonymity"`
	Imgs           []Img    `bson:"imgs" json:"imgs"`
	LikedUsers     []string `bson:"liked_users" json:"liked_users"`
	LikedUserNum   int      `bson:"liked_user_num" json:"liked_user_num"`
	Score          int      `bson:"score" json:"score"`
	ReplyNum       int      `bson:"reply_num" json:"reply_num"`
	RecommenderNum int      `bson:"recommender_num" json:"recommender_num"`
	Recommender    []string `bson:"recommender" json:"recommender"`
	IssueTime      int64    `bson:"issue_time" json:"issue_time"`
	Islike         bool     `bson:"islike" json:"islike"`
	Userinfo       User     `bson:"userinfo" json:"userinfo"`
	IsApplication  bool     `bson:"is_application" json:"is_application"`
	NewID          int      `json:"new_id" bson:"new_id"`
}

type ForumpostNum struct {
	Lock sync.Mutex
	Num  int
}

var Num *ForumpostNum

func Forumpostinit() {
	Num = &ForumpostNum{}
	log.Println("start init ForumpostNum config")
	Num.Num = 0
	sess := globalSess.Clone()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(FORUMPOSTTABLE)
	Num.Num, _ = commentTable.Count()
	log.Println("over init ForumpostNum config  Num: ", Num.Num)
}

func ChangeType(id string, tags []string) error {
	query := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$set": bson.M{
			"type":      2,
			"type_tags": tags,
		},
	}
	return updateForumPost(query, update)
}

func ChangeApplication(id string) error {
	query := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$set": bson.M{
			"is_application": true,
		},
	}
	return updateForumPost(query, update)
}

func CreateForumPost(forumpost ForumPost) (ForumPost, error) {
	forumpost.Userinfo = User{}
	forumpost.IssueTime = newtime.GetMilliTimestamp()
	forumpost.IsApplication = false
	forumpost.Score = 0
	for i := 0; i < len(forumpost.Tags); i++ {
		forumpost.Score += config.TagsScore[forumpost.Tags[i]]
	}
	forumpost.Status = config.ForumPostCommonStatus
	forumpost.Type = 1
	forumpost.TypeTags = []int{}
	forumpost.ReplyNum = 0
	forumpost.Recommender = nil
	forumpost.RecommenderNum = 0

	Num.Lock.Lock()
	Num.Num++
	forumpost.NewID = Num.Num

	err := insertForumPost(forumpost)
	if err != nil {
		Num.Num--
		Num.Lock.Unlock()
		return ForumPost{}, err
	}
	Num.Lock.Unlock()
	query := bson.M{
		"issue_time": forumpost.IssueTime,
		"user_id":    forumpost.UserID,
	}
	return findForumPost(query, DefaultSelector)
}

func CreateForumPost1(forumpost ForumPost) (ForumPost, error) {
	forumpost.Userinfo = User{}
	forumpost.IssueTime = newtime.GetMilliTimestamp()
	forumpost.IsApplication = false
	forumpost.Score = 0
	for i := 0; i < len(forumpost.Tags); i++ {
		forumpost.Score += config.TagsScore[forumpost.Tags[i]]
	}
	forumpost.Status = config.ForumPostCommonStatus
	forumpost.Type = 2
	forumpost.ReplyNum = 0
	forumpost.Recommender = nil
	forumpost.RecommenderNum = 0

	Num.Lock.Lock()
	Num.Num++
	forumpost.NewID = Num.Num

	err := insertForumPost(forumpost)
	if err != nil {
		Num.Num--
		Num.Lock.Unlock()
		return ForumPost{}, err
	}
	Num.Lock.Unlock()
	query := bson.M{
		"issue_time": forumpost.IssueTime,
		"user_id":    forumpost.UserID,
		"text":       forumpost.Text,
	}
	return findForumPost(query, DefaultSelector)
}

func DeleteForumPost(id, userid string, admin bool) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}
	var query bson.M
	if admin {
		query = bson.M{
			"_id": bson.ObjectIdHex(id),
		}
	} else {
		query = bson.M{
			"_id":     bson.ObjectIdHex(id),
			"user_id": userid,
		}
	}
	update := bson.M{
		"$set": bson.M{
			"status": config.ForumPostDeleteStatus,
		},
	}
	return updateForumPost(query, update)
}

func StarForumPost(id, user_id string, flag bool) error {
	if flag {
		return likeForumPost(id, user_id)
	}
	return cancelLikeForumPost(id, user_id)
}
func likeForumPost(id, user_id string) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}

	findQuery := bson.M{
		"_id":         bson.ObjectIdHex(id),
		"liked_users": user_id,
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"liked_user_num": 1,
		},
		"$push": bson.M{
			"liked_users": user_id,
		},
	}

	return ForumPostAction(findQuery, updateQuery, update)
}
func cancelLikeForumPost(id, user_id string) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}

	findQuery := bson.M{
		"_id":         bson.ObjectIdHex(id),
		"liked_users": user_id,
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"liked_user_num": -1,
		},
		"$pull": bson.M{
			"liked_users": user_id,
		},
	}
	return ForumPostAction2(findQuery, updateQuery, update)
}
func ForumPostAction(findQuery, updateQuery, update interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	Table := sess.DB(DBNAME).C(FORUMPOSTTABLE)

	var fp ForumPost
	err := Table.Find(findQuery).One(&fp)
	if err == nil {
		return config.ErrorHasExist
	}

	return Table.Update(updateQuery, update)
}
func ForumPostAction2(findQuery, updateQuery, update interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	Table := sess.DB(DBNAME).C(FORUMPOSTTABLE)

	var fp ForumPost
	err := Table.Find(findQuery).One(&fp)
	if err != nil {
		return err
	}

	return Table.Update(updateQuery, update)
}

func GetForumpostByUserid(id string, page, perpage int, user_id string) ([]ForumPost, error) {
	query := bson.M{
		"user_id": user_id,
		"status":  config.ForumPostCommonStatus,
	}
	fields := []string{
		"-issue_time", "-score",
	}
	forumpostposts, err := findForumPosts(query, DefaultSelector, page, perpage, fields...)
	if err != nil {
		return []ForumPost{}, err
	}
	user, _ := GetUserInfo(user_id)
	for i := 0; i < len(forumpostposts); i++ {
		forumpostposts[i].Islike = false
		if user_id != "" {
			for _, s := range forumpostposts[i].LikedUsers {
				if s == user_id {
					forumpostposts[i].Islike = true
					break
				}
			}
		}
		forumpostposts[i].Userinfo = user
		//if forumpostposts[i].Anonymity == true {
		//	forumpostposts[i].UserID = ""
		//} else {
		//	forumpostposts[i].Userinfo, err = GetUserInfo(forumpostposts[i].UserID)
		//}
	}
	return forumpostposts, err
}

func GetForumpost_score(t int, key string, tags int, page, perpage int, id string) ([]ForumPost, error) {
	var query bson.M
	if tags == 0 {
		query = bson.M{
			"type": bson.M{
				"$gte": t,
			},
			"text": bson.M{
				"$regex": bson.RegEx{
					Pattern: key,
					Options: "i",
				},
			},
			"status": config.ForumPostCommonStatus,
		}
	} else {
		if t == 1 {
			query = bson.M{
				"type": bson.M{
					"$gte": t,
				},
				"tags": tags,
				"text": bson.M{
					"$regex": bson.RegEx{
						Pattern: key,
						Options: "i",
					},
				},
				"status": config.ForumPostCommonStatus,
			}
		} else {
			query = bson.M{
				"type": bson.M{
					"$gte": t,
				},
				"type_tags": tags,
				"text": bson.M{
					"$regex": bson.RegEx{
						Pattern: key,
						Options: "i",
					},
				},
				"status": config.ForumPostCommonStatus,
			}
		}
	}
	fields := []string{
		"-score", "-issue_time",
	}
	forumpostposts, err := findForumPosts(query, DefaultSelector, page, perpage, fields...)
	for i := 0; i < len(forumpostposts); i++ {
		forumpostposts[i].Islike = false
		forumpostposts[i].IsApplication = false
		if id != "" {
			for _, s := range forumpostposts[i].LikedUsers {
				if s == id {
					forumpostposts[i].Islike = true
					break
				}
			}
		}
		if id != "" {
			for _, s := range forumpostposts[i].Recommender {
				if s == id {
					forumpostposts[i].IsApplication = true
					break
				}
			}
		}
		if forumpostposts[i].Anonymity == true && id != forumpostposts[i].UserID {
			forumpostposts[i].UserID = ""
		} else {
			forumpostposts[i].Userinfo, err = GetUserInfo(forumpostposts[i].UserID)
		}
	}
	return forumpostposts, err
}
func GetForumpost_time(t int, key string, tags int, page, perpage int, id string) ([]ForumPost, error) {
	var query bson.M
	if tags == 0 {
		query = bson.M{
			"type": bson.M{
				"$gte": t,
			},
			"text": bson.M{
				"$regex": bson.RegEx{
					Pattern: key,
					Options: "i",
				},
			},
			"status": config.ForumPostCommonStatus,
		}
	} else {
		if t == 1 {
			query = bson.M{
				"type": bson.M{
					"$gte": t,
				},
				"tags": tags,
				"text": bson.M{
					"$regex": bson.RegEx{
						Pattern: key,
						Options: "i",
					},
				},
				"status": config.ForumPostCommonStatus,
			}
		} else {
			query = bson.M{
				"type": bson.M{
					"$gte": t,
				},
				"type_tags": tags,
				"text": bson.M{
					"$regex": bson.RegEx{
						Pattern: key,
						Options: "i",
					},
				},
				"status": config.ForumPostCommonStatus,
			}
		}
	}
	fields := []string{
		"-issue_time", "score",
	}
	fps, err := findForumPosts(query, DefaultSelector, page, perpage, fields...)
	for i := 0; i < len(fps); i++ {
		fps[i].Islike = false
		fps[i].IsApplication = false
		if id != "" {
			for _, s := range fps[i].LikedUsers {
				if s == id {
					fps[i].Islike = true
					break
				}
			}
		}
		if id != "" {
			for _, s := range fps[i].Recommender {
				if s == id {
					fps[i].IsApplication = true
					break
				}
			}
		}
		if fps[i].Anonymity == true && id != fps[i].UserID {
			fps[i].UserID = ""
		} else {
			fps[i].Userinfo, err = GetUserInfo(fps[i].UserID)
		}
	}
	return fps, err
}

func GetForumpostByid(id string, user_id string) (ForumPost, error) {
	if !bson.IsObjectIdHex(id) {
		return ForumPost{}, config.ErrorIDFormatWrong
	}
	query := bson.M{
		"_id":    bson.ObjectIdHex(id),
		"status": config.ForumPostCommonStatus,
	}
	post, err := findForumPost(query, DefaultSelector)
	if err != nil {
		return ForumPost{}, err
	}
	post.Islike = false
	if user_id != "" {
		for _, s := range post.LikedUsers {
			if s == user_id {
				post.Islike = true
				break
			}
		}
	}
	if user_id != "" {
		for _, s := range post.Recommender {
			if s == user_id {
				post.IsApplication = true
				break
			}
		}
	}
	if post.Anonymity && post.UserID != user_id {
		post.UserID = ""
	} else {
		post.Userinfo, _ = GetUserInfo(post.UserID)
	}
	return post, err
}

func UpdateForumpostReply(id string, value int) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"reply_num": value,
		},
	}
	return ForumPostAction2(updateQuery, updateQuery, update)
}
func UpdateForumpostScore(id string, value int) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"score": value,
		},
	}
	return ForumPostAction2(updateQuery, updateQuery, update)
}

func UpdateForumpostRecommend(id string, user_id string) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}
	findQuery := bson.M{
		"_id":         bson.ObjectIdHex(id),
		"recommender": user_id,
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"recommender_num": 1,
		},
		"$push": bson.M{
			"recommender": user_id,
		},
	}
	return ForumPostAction(findQuery, updateQuery, update)
}

/**********************************************************************/
func updateForumPost(query, update interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(FORUMPOSTTABLE)

	return commentTable.Update(query, update)
}
func insertForumPost(docs ...interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	ForumPostTable := sess.DB(DBNAME).C(FORUMPOSTTABLE)

	return ForumPostTable.Insert(docs...)
}
func findForumPost(query, selector interface{}) (ForumPost, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	ForumPostTable := sess.DB(DBNAME).C(FORUMPOSTTABLE)

	ForumPost := ForumPost{}
	err := ForumPostTable.Find(query).Select(selector).One(&ForumPost)
	return ForumPost, err
}
func findForunpostsALL(query interface{}) ([]ForumPost, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	Table := sess.DB(DBNAME).C(FORUMPOSTTABLE)

	ForumPosts := []ForumPost{}
	err := Table.Find(query).All(&ForumPosts)
	return ForumPosts, err
}
func findForumPosts(query, selector interface{}, page, perPage int, fields ...string) ([]ForumPost, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	ForumPostTable := sess.DB(DBNAME).C(FORUMPOSTTABLE)

	ForumPosts := []ForumPost{}
	err := ForumPostTable.Find(query).Select(selector).Sort(fields...).Skip((page - 1) * perPage).Limit(perPage).All(&ForumPosts)
	return ForumPosts, err
}
